package pers.yurwisher.rubick.manager.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pers.yurwisher.rubick.common.base.impl.BaseServiceImpl;
import pers.yurwisher.rubick.common.constant.CacheConstant;
import pers.yurwisher.rubick.common.enums.tip.SystemCustomTipEnum;
import pers.yurwisher.rubick.common.exception.SystemCustomException;
import pers.yurwisher.rubick.common.service.CustomRedisCacheService;
import pers.yurwisher.rubick.common.user.CurrentUser;
import pers.yurwisher.rubick.common.user.CustomUserDetails;
import pers.yurwisher.rubick.common.utils.PudgeUtil;
import pers.yurwisher.rubick.common.wrapper.PageR;
import pers.yurwisher.rubick.manager.entity.SystemUser;
import pers.yurwisher.rubick.manager.mapper.SystemUserMapper;
import pers.yurwisher.rubick.manager.pojo.fo.ChangeAccountInfoFo;
import pers.yurwisher.rubick.manager.pojo.fo.ChangeMailFo;
import pers.yurwisher.rubick.manager.pojo.fo.ChangePhoneFo;
import pers.yurwisher.rubick.manager.pojo.fo.ResetPasswordFo;
import pers.yurwisher.rubick.manager.pojo.fo.SystemUserFo;
import pers.yurwisher.rubick.manager.pojo.qo.SystemUserQo;
import pers.yurwisher.rubick.manager.pojo.to.SystemUserTo;
import pers.yurwisher.rubick.manager.pojo.vo.SystemUserVo;
import pers.yurwisher.rubick.manager.service.IRelationService;
import pers.yurwisher.rubick.manager.service.ISystemRoleService;
import pers.yurwisher.rubick.manager.service.ISystemUserService;

import java.time.LocalDateTime;
import java.util.List;

/**
 * @author yq
 * @date 2020/09/21 11:36
 * @description 系统用户service
 * @since V1.0.0
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SystemUserServiceImpl extends BaseServiceImpl<SystemUserMapper, SystemUser> implements ISystemUserService {

    private final ISystemRoleService systemRoleService;
    private final IRelationService relationService;
    private final CustomRedisCacheService customRedisCacheService;

    @Override
    public CurrentUser getById(Long userId) {
        return customRedisCacheService.hashRound(CacheConstant.Hashs.SYSTEM_USER_INFO, userId.toString(), () -> {
            SystemUser systemUser = baseMapper.selectById(userId);
            CurrentUser vo = new CurrentUser();
            BeanUtils.copyProperties(systemUser, vo);
            //获取用户角色
            List<String> roles = systemRoleService.getUserRole(userId);
            vo.setRoles(roles);
            //是否管理员
            vo.setAdmin(CollectionUtil.isNotEmpty(roles) && roles.contains("admin"));
            return vo;
        }, null);
    }

    @Override
    public SystemUser getUserByUsername(String username) {
        return super.getOneByFieldValueEq(SystemUser::getUsername, username);
    }

    @Override
    @SuppressWarnings("unchecked")
    public PageR<SystemUserTo> list(SystemUserQo qo) {
        return super.toPageR(baseMapper.list(super.toPage(qo), qo));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(SystemUserFo fo) {
        //判断用户名是否存在
        if (super.haveFieldValueEq(SystemUser::getUsername, fo.getUsername())) {
            //用户名已被使用
            throw new SystemCustomException(SystemCustomTipEnum.USERNAME_EXISTED);
        }
        //判断手机号是否存在
        if (super.haveFieldValueEq(SystemUser::getPhone, fo.getPhone())) {
            //用户名已被使用
            throw new SystemCustomException(SystemCustomTipEnum.PHONE_EXISTED);
        }
        log.info("新增用户: [{}]", fo.getUsername());
        SystemUser user = new SystemUser();
        BeanUtils.copyProperties(fo, user);
        //Todo 给予初始密码
        user.setPassword("");
        //启用.正常
        user.setState(1);
        baseMapper.insert(user);
        //绑定角色
        relationService.userBindRoles(user.getId(), fo.getRoleIds());
        this.userInfoChangeRemoveCache(user.getUsername());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Long id, SystemUserFo fo) {
        SystemUser user = baseMapper.selectById(id);
        Assert.notNull(user);
        SystemUser userByPhone = super.getOneByFieldValueEq(SystemUser::getPhone, fo.getPhone());
        //判断手机号是否存在
        if (userByPhone != null && !userByPhone.getId().equals(id)) {
            //用户名已被使用
            throw new SystemCustomException(SystemCustomTipEnum.PHONE_EXISTED);
        }
        BeanUtils.copyProperties(fo, user);
        baseMapper.updateById(user);
        //绑定角色
        relationService.userBindRoles(user.getId(), fo.getRoleIds());
        this.userInfoChangeRemoveCache(user.getUsername());
    }

    @Override
    public SystemUserVo get(Long id) {
        SystemUserVo vo = baseMapper.get(id);
        vo.setRoleIds(relationService.getUserAlreadyBindRoleIds(id));
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetPassword(Long id) {
        SystemUser user = baseMapper.selectById(id);
        Assert.notNull(user);
        this.update(Wrappers.<SystemUser>lambdaUpdate()
                .set(SystemUser::getPassword, "")
                .set(SystemUser::getLastUpdated, LocalDateTime.now())
                .eq(SystemUser::getId, id));
        //移除在线用户缓存
        this.userInfoChangeRemoveCache(user.getUsername());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePassword(ResetPasswordFo resetPasswordFo) {
        CurrentUser currentUser = CustomUserDetails.current();
        // 密码解密
        String oldPass = this.decryptRsaPassword(resetPasswordFo.getOldPass());
        //旧密码错误
        if (!currentUser.getPassword().equals(PudgeUtil.encodePwd(oldPass))) {
            throw new SystemCustomException(SystemCustomTipEnum.AUTH_OLD_PASS_ERROR);
        }
        //2次密码不一致
        if (!resetPasswordFo.getNewPass().equals(resetPasswordFo.getConfirmPass())) {
            throw new SystemCustomException(SystemCustomTipEnum.AUTH_TWO_PASS_NOT_EQUAL);
        }
        log.info("用户[{}]变更密码", currentUser.getId());
        String newPass = this.decryptRsaPassword(resetPasswordFo.getNewPass());
        String currentUsername = currentUser.getUsername();
        this.update(Wrappers.<SystemUser>lambdaUpdate()
                .set(SystemUser::getPassword, PudgeUtil.encodePwd(newPass))
                .set(SystemUser::getLastUpdated, LocalDateTime.now())
                .eq(SystemUser::getId, currentUser.getId())
        );
        //移除缓存
        this.userInfoChangeRemoveCache(currentUsername);
    }

    @Override
    public void sendChangeMailCode(String mail) {
        if (StrUtil.isNotBlank(mail)) {
            //6位验证码
            String mailValidCode = RandomUtil.randomNumbers(6);
            log.info("变更邮箱发送验证码 [{}] 到 [{}]", mailValidCode, mail);
            //存入redis
            customRedisCacheService.set(CacheConstant.Strings.CHANGE_MAIL_CODE, mail, () -> mailValidCode, null);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeMail(ChangeMailFo changeMailFo) {
        CurrentUser currentUser = CustomUserDetails.current();
        //新邮箱与旧邮箱相同
        if (changeMailFo.getMail().equals(currentUser.getMail())) {
            throw new SystemCustomException(SystemCustomTipEnum.AUTH_NEW_MAIL_EQUAL_OLD);
        }
        // 密码解密
        String oldPass = this.decryptRsaPassword(changeMailFo.getPassword());
        //判断密码
        if (!PudgeUtil.encodePwd(oldPass).equals(currentUser.getPassword())) {
            throw new SystemCustomException(SystemCustomTipEnum.AUTH_CURRENT_PASS_ERROR);
        }
        //判断验证码
        customRedisCacheService.getAndDel(CacheConstant.Strings.CHANGE_MAIL_CODE, changeMailFo.getMail(), (String code) -> {
            if (StrUtil.isBlank(code)) {
                //邮件验证码过期
                throw new SystemCustomException(SystemCustomTipEnum.AUTH_CODE_NOT_EXIST_OR_EXPIRED);
            }
            String codeVal = changeMailFo.getCode();
            if (StrUtil.isBlank(codeVal) || !codeVal.equalsIgnoreCase(code)) {
                //邮件验证码错误
                throw new SystemCustomException(SystemCustomTipEnum.AUTH_CODE_ERROR);
            }
        });
        log.info("用户[{}]变更邮箱,从[{}]->[{}]}", currentUser.getId(), currentUser.getMail(), changeMailFo.getMail());
        this.update(Wrappers.<SystemUser>lambdaUpdate()
                .set(SystemUser::getMail, changeMailFo.getMail())
                .set(SystemUser::getLastUpdated, LocalDateTime.now())
                .eq(SystemUser::getId, currentUser.getId())
        );
        //移除缓存
        this.userInfoChangeRemoveCache(currentUser.getUsername());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeAccountInfo(ChangeAccountInfoFo changeAccountInfoFo) {
        CurrentUser currentUser = CustomUserDetails.current();
        //昵称没改 直接返回
        if (currentUser.getNickname().equals(changeAccountInfoFo.getNickname())) {
            return;
        }
        log.info("用户[{}]变更帐号信息", currentUser.getId());
        this.update(Wrappers.<SystemUser>lambdaUpdate()
                .set(SystemUser::getNickname, changeAccountInfoFo.getNickname())
                .set(SystemUser::getLastUpdated, LocalDateTime.now())
                .eq(SystemUser::getId, currentUser.getId())
        );
        //移除缓存
        this.userInfoChangeRemoveCache(currentUser.getUsername());
    }

    @Override
    public void sendChangePhoneCode(String phone) {
        if (StrUtil.isNotBlank(phone)) {
            //6位验证码
            String phoneValidCode = RandomUtil.randomNumbers(6);
            log.info("更换绑定手机发送验证码 [{}] 到 [{}]", phoneValidCode, phone);
            //先取已有->无则重发->存入redis
            customRedisCacheService.set(CacheConstant.Strings.CHANGE_PHONE_CODE, phone, () -> phoneValidCode, null);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePhone(ChangePhoneFo changePhoneFo) {
        CurrentUser currentUser = CustomUserDetails.current();
        //新旧手机相同
        if (changePhoneFo.getPhone().equals(currentUser.getPhone())) {
            throw new SystemCustomException(SystemCustomTipEnum.AUTH_NEW_PHONE_EQUAL_OLD);
        }
        // 密码解密
        String oldPass = this.decryptRsaPassword(changePhoneFo.getPassword());
        //判断密码
        if (!PudgeUtil.encodePwd(oldPass).equals(currentUser.getPassword())) {
            throw new SystemCustomException(SystemCustomTipEnum.AUTH_CURRENT_PASS_ERROR);
        }
        //判断验证码
        customRedisCacheService.getAndDel(CacheConstant.Strings.CHANGE_PHONE_CODE, changePhoneFo.getPhone(), (String code) -> {
            if (StrUtil.isBlank(code)) {
                //短信验证码过期
                throw new SystemCustomException(SystemCustomTipEnum.AUTH_CODE_NOT_EXIST_OR_EXPIRED);
            }
            String codeVal = changePhoneFo.getCode();
            if (StrUtil.isBlank(codeVal) || !codeVal.equalsIgnoreCase(code)) {
                //短信验证码错误
                throw new SystemCustomException(SystemCustomTipEnum.AUTH_CODE_ERROR);
            }
        });
        log.info("用户[{}]变更绑定手机,从[{}]->[{}]}", currentUser.getId(), currentUser.getPhone(), changePhoneFo.getPhone());
        this.update(Wrappers.<SystemUser>lambdaUpdate()
                .set(SystemUser::getPhone, changePhoneFo.getPhone())
                .set(SystemUser::getLastUpdated, LocalDateTime.now())
                .eq(SystemUser::getId, currentUser.getId())
        );
        //移除缓存
        this.userInfoChangeRemoveCache(currentUser.getUsername());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void switchState(Long id, Integer state) {
        SystemUser user = baseMapper.selectById(id);
        Assert.notNull(user);
        if (state.equals(user.getState())) {
            return;
        }
        log.info("用户[{}]变更状态,从[{}]->[{}]}", id, user.getState(), state);
        this.update(Wrappers.<SystemUser>lambdaUpdate()
                .set(SystemUser::getState, state)
                .set(SystemUser::getLastUpdated, LocalDateTime.now())
                .eq(SystemUser::getId, id));
        this.userInfoChangeRemoveCache(user.getUsername());
    }


    private String decryptRsaPassword(String rsaPassword) {
        return rsaPassword;
    }

    /**
     * 用户信息变更 移除相应缓存
     *
     * @param username 用户名
     */
    private void userInfoChangeRemoveCache(String username) {
        customRedisCacheService.hDel(CacheConstant.Hashs.SYSTEM_USER_INFO, username);
    }
}
